﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using System.ComponentModel;
using System.Xml.Xsl;
using System.Xml;
using System.IO;
using System.Net;
using System.Collections.Specialized;

namespace SharePointPowerToys
{
    public class XmlPowerViewer : Microsoft.SharePoint.WebPartPages.WebPart, IWebEditable
    {
        private string xml = string.Empty;
        private string xsl = string.Empty;

        #region WebPart.Properties
        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XML
        {
            get;
            set;
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XMLLink
        {
            get;
            set;
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XMLLinkUsername
        {
            get;
            set;
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XMLLinkPassword
        {
            get;
            set;
        }

        [WebBrowsable(false),
        DefaultValue(0),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public long CacheSourceSeconds
        {
            get;
            set;
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XMLCacheKey
        {
            get;
            set;
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XSL
        {
            get;
            set;
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XSLLink
        {
            get;
            set;
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XSLLinkUsername
        {
            get;
            set;
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XSLLinkPassword
        {
            get;
            set;
        }


        [WebBrowsable(false),
        DefaultValue(0),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public long XSLResultsCacheSeconds
        {
            get;
            set;
        }
        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public string XSLResultsCacheKey
        {
            get;
            set;
        }

        [WebBrowsable(false),
        DefaultValue(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public bool XSLClientTransform
        {
            get;
            set;
        }

        [WebBrowsable(false),
        DefaultValue(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorageAttribute(Storage.Shared)]
        public bool DebugMode
        {
            get;
            set;
        }
        #endregion

        /// <summary>
        /// Called by the ASP.NET page framework to create any child controls 
        /// in preparation for posting back or rendering. 
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            this.ChildControlsCreated = true;
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            string results = string.Empty;
            StringBuilder debug = new StringBuilder();

            debug.Append(string.Concat("\r\n<!--Generating WebPart: ", this.Title, "-->\r\n"));

            try
            {
                // Results Cached?
                
                string _XSLResultsCacheKey = String.Concat(String.IsNullOrEmpty(XSLResultsCacheKey) ? ID : XSLResultsCacheKey, "_Results");
                string _XMLCacheKey = String.Concat(String.IsNullOrEmpty(XMLCacheKey) ? ID : XMLCacheKey, "_SourceXML");

                NameValueCollection tokens = Helpers.SwapTokensGetDefaultTokens();
                tokens.Add("{WebPartTitle}", this.Title);
                debug.Append(string.Concat("\r\n<!--Set token: {WebPartTitle}=", this.Title, "-->\r\n"));

                _XSLResultsCacheKey = Helpers.SwapTokens(_XSLResultsCacheKey, tokens);
                _XMLCacheKey = Helpers.SwapTokens(_XMLCacheKey, tokens);

                if (XSLResultsCacheSeconds > 0)
                {
                    results = CacheRead(_XSLResultsCacheKey);
                }

                if (!String.IsNullOrEmpty(results))
                {
                    debug.Append(string.Concat("\r\n<!--Read from results cache:", _XSLResultsCacheKey, "-->\r\n"));
                }
                else
                {
                    debug.Append(string.Concat("\r\n<!--Read from results cache:", _XSLResultsCacheKey, " failed-->\r\n"));

                    // Data Source Cached?
                    if (CacheSourceSeconds > 0)
                    {
                        xml = CacheRead(_XMLCacheKey);
                    }

                    if(String.IsNullOrEmpty(xml))
                    {
                        debug.Append(string.Concat("\r\n<!--Read from source cache:", _XMLCacheKey, " failed-->\r\n"));

                        if (!String.IsNullOrEmpty(XMLLink))
                        {
                            try
                            {
                                string baseUrl = Helpers.GetWebUrlFromUrl(HttpContext.Current.Request.Url.ToString());
                                debug.Append(string.Concat("\r\n<!--XML loading from baseUrl:", baseUrl, " -->\r\n"));
                                if (!XMLLink.StartsWith("http"))
                                {
                                    string xmlLink = String.Concat(baseUrl, "/_layouts/PowerRSS.aspx?List=", XMLLink);
                                    debug.Append(string.Concat("\r\n<!--XML loading from XML 1Link:", xmlLink, " -->\r\n"));
                                    xml = Helpers.GetFile(xmlLink, XMLLinkUsername, XMLLinkPassword);
                                }
                                else
                                {
                                    debug.Append(string.Concat("\r\n<!--XML loading from XML Link: ", String.Concat(HttpContext.Current.Request.Url.ToString().Substring(0, HttpContext.Current.Request.Url.ToString().LastIndexOf("/")), "/_layouts/rss.aspx?List=", XMLLink), " -->\r\n"));
                                    xml = Helpers.GetFile(XMLLink, XMLLinkUsername, XMLLinkPassword);
                                    debug.Append(string.Concat("\r\n<!--XML loaded from XML Link: ", xml, " -->\r\n"));
                                }
                            }
                            catch (Exception ex)
                            {
                                debug.Append(String.Concat("\r\n<!--Error reading XMLLink: ", XMLLink, " - ", ex.Message, " -->\r\n"));
                            }
                            if (String.IsNullOrEmpty(xml) && !String.IsNullOrEmpty(XML))
                            {
                                xml = XML;
                                debug.Append("\r\n<!--XML loaded form XML Editor -->\r\n");
                            }
                            else
                            {
                                debug.Append("\r\n<!--XML loaded form XML Link -->\r\n");
                            }
                            debug.Append(String.Concat("\r\n<!--XML:", xml, "-->\r\n"));
                        }
                    }

                    xml = Helpers.SwapTokens(xml, tokens);

                    debug.Append(string.Concat("\r\n<!--XML:", xml, "-->\r\n"));

                    // Cache XmlReader if necessary
                    if (CacheSourceSeconds > 0 && !String.IsNullOrEmpty(xml))
                    {
                        try
                        {
                            CacheSave(_XMLCacheKey, xml, (XMLCacheKey == string.Empty ? false : true), CacheSourceSeconds);
                            debug.Append(string.Concat("\r\n<!--Wrote to cache:", _XMLCacheKey, "-->\r\n"));
                        }
                        catch (Exception ex)
                        {
                            debug.Append(string.Concat("\r\n<!--Error saving to source cache:", ex.Message, "-->\r\n"));
                        }
                    }

                    // Load the XSLT
                    if (!String.IsNullOrEmpty(XSLLink))
                    {
                        try
                        {
                            debug.Append(string.Concat("\r\n<!--XSL Link:", XSLLink, "-->\r\n"));

                            xsl = Helpers.GetFile(XSLLink, XSLLinkUsername, XSLLinkPassword);

                            debug.Append(string.Concat("\r\n<!--XSL:", xsl, "-->\r\n"));
                        }
                        catch (Exception ex)
                        {
                            debug.Append(string.Concat("\r\n<!--Error:", ex.Message, "-->\r\n"));
                        }
                    }
                    if (String.IsNullOrEmpty(xsl))
                    {
                        xsl = XSL;
                        debug.Append("\r\n<!--XSL loaded form XSL Editor-->\r\n");
                    }

                    xsl = Helpers.SwapTokens(xsl, tokens);
                    
                    debug.Append(string.Concat("\r\n<!--XSL final:", xsl, "-->\r\n"));
                    
                    // Run the XSLT
                    if (!String.IsNullOrEmpty(xsl))
                    {
                        try
                        {
                            results = Helpers.TransformGetString(xsl, XSLLinkUsername, XSLLinkPassword, xml, XMLLinkUsername, XMLLinkPassword);
                        }
                        catch (Exception ex)
                        {
                            Controls.Add(new LiteralControl(String.Concat("Could not load XML from Transformation using XSL Style Sheet: ", XSLLink, " - Error: ", ex.Message)));
                            return;
                        }
                    }

                    // CacheResults
                    if (XSLResultsCacheSeconds > 0)
                    {
                        CacheSave(_XSLResultsCacheKey, results, (XSLResultsCacheKey == string.Empty ? false : true), XSLResultsCacheSeconds);
                        debug.Append(string.Concat("\r\n<!--Wrote to cache:", _XSLResultsCacheKey, "-->\r\n"));
                    }
                }
            }
            catch (Exception ex)
            {
                debug.Append(String.Concat("\r\n<!--Error: ", ex.Message, "</br>StackTrace: ", ex.StackTrace, "-->\r\n"));
            }

            writer.Write(results);
            if (DebugMode) writer.Write(debug.ToString());
            base.RenderContents(writer);
            this.EnsureChildControls();
        }

        /// <summary>
        /// Raises the System.Web.UI.Control.PreRender event.
        /// Use this to register any data loads in asyncronous tasks.
        /// </summary><param name="e">An System.EventArgs object that contains the event data.</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            //TODO: Register async tasks here
        }

        private string CacheRead(string key)
        {
            //object cacheItem = PartCacheRead(Storage.Shared, key);
            object cacheItem = HttpContext.Current.Cache[key];
            if (cacheItem != null)
            {
                return cacheItem.ToString();
            }
            return string.Empty;
        }


        private void CacheSave(string key, string value, long secondsToCache)
        {
            CacheSave(key, value, false, secondsToCache);
        }

        private void CacheSave(string key, string value, bool higherPriority, long secondsToCache)
        {
            //PartCacheWrite(Storage.Shared, key, value, timeout);
            //HttpContext.Current.Cache.Add(key, value, null, DateTime.Now.AddSeconds(secondsToCache), TimeSpan.Zero, CacheItemPriority.Normal, null);
            if (higherPriority)
            {
                HttpContext.Current.Cache.Add(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(secondsToCache), CacheItemPriority.AboveNormal, null);
            }
            else
            {
                HttpContext.Current.Cache.Add(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(secondsToCache), CacheItemPriority.Normal, null);
            }
            //Context.Cache.Insert(key, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(secondsToCache));
        }

        #region IWebEditable Members

        EditorPartCollection IWebEditable.CreateEditorParts()
        {

            if (this.WebPartManager.Personalization.Scope == PersonalizationScope.Shared)
            {

                var editors = new List<EditorPart> { new CustomEditorPart(ID) };
                return new EditorPartCollection(editors);
            }
            else
            {
                return null;
            }
        }

        object IWebEditable.WebBrowsableObject
        {
            get { return this; }
        }

        #endregion
    }
}
